﻿using DocumentFormat.OpenXml.Lite.DrawingML;
using DocumentFormat.OpenXml.Lite.DrawingML.Chart;
using DocumentFormat.OpenXml.Lite.DrawingML.Chart.Axes;
using DocumentFormat.OpenXml.Lite.DrawingML.Chart.Elements;
using DocumentFormat.OpenXml.Lite.DrawingML.Chart.Properties;
using DocumentFormat.OpenXml.Lite.DrawingML.Color;
using DocumentFormat.OpenXml.Lite.DrawingML.Effect;
using DocumentFormat.OpenXml.Lite.DrawingML.Font;
using DocumentFormat.OpenXml.Lite.DrawingML.Geometry;
using DocumentFormat.OpenXml.Lite.DrawingML.Geometry.Properties;
using DocumentFormat.OpenXml.Lite.DrawingML.Text;
using DocumentFormat.OpenXml.Lite.DrawingML.Text.Properties;
using DocumentFormat.OpenXml.Lite.DrawingML.Transformation;
using DocumentFormat.OpenXml.Lite.DrawingML.Types;
using DocumentFormat.OpenXml.Lite.DrawingML.Visual;
using DocumentFormat.OpenXml.Lite.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Samples.Drawing
{
    class SampleChartBuilder
    {
        public static ChartSpace CreateChart(ChartDescriptor descriptor)
        {
            SchemeColorModel colorModel = new SchemeColorModel();
            colorModel.ColorValue = ST_SchemeColorVal.TextColor1;
            colorModel.ColorElements.Add(new LuminanceModulation() { Value = 15000 });
            colorModel.ColorElements.Add(new LuminanceOffset() { Value = 85000 });

            ChartSpace chartspace = new ChartSpace()
            {
                Date1904Format = new NullableValue<bool>(false),
                RoundedCorners = new NullableValue<bool>(false),
                Chart = Create(descriptor),
                ShapeProperties = new ShapeProperties()
                {
                    FillProperty = new SolidFill()
                    {
                        ColorElement = new SchemeColorModel()
                        {
                            ColorValue = ST_SchemeColorVal.BackgroundColor1
                        },
                    },
                    Line = new Line()
                    {
                        LineFillProperty = new SolidFill()
                        {
                            ColorElement = colorModel
                        },
                        LineJoinProperty = new RoundLineJoin()
                    },
                    EffectProperty = new EffectList()
                },
                TextProperties = new TextProperties()
                {
                    BodyProperties = new BodyProperties(),
                    TextListStyles = new TextListStyles()
                }
            };

            Paragraph p = new Paragraph();
            p.Properties = new ParagraphProperties()
            {
                RunProperties = new RunProperties()
            };
            p.RunProperties = new RunProperties() { LanguageId = "hu-HU" };
            chartspace.TextProperties.Paragraphs.Add(p);

            return chartspace;
        }

        private static Chart Create(ChartDescriptor descriptor)
        {
            Random r = new Random();
            uint xaxis = (uint)r.Next(100000);
            uint yaxis = (uint)r.Next(100000);

            Chart chart = new Chart()
            {
                Title = CreateTitle(descriptor.Title),
                Legend = CreateLegend(),
                PlotArea = CreatePlotArea(xaxis, yaxis, descriptor),
                AutoTitleDeleted = new NullableValue<bool>(false),
                PlotVisibleOnly = new NullableValue<bool>(true),
                DisplayBlanksAs = new NullableValue<ST_DispBlanksAs>(ST_DispBlanksAs.Gap),
                ShowDataLabelsOverMaximum = new NullableValue<bool>(false)
            };

            chart.PlotArea.Charts.Add(CreateLineChart(xaxis, yaxis, descriptor, 0));

            return chart;
        }

        private static Title CreateTitle(string title)
        {
            ShapeProperties shapeProperties = new ShapeProperties()
            {
                FillProperty = new NoFill(),
                Line = new Line() { LineFillProperty = new NoFill() },
                EffectProperty = new EffectList()
            };

            TextProperties textProperties = new TextProperties()
            {
                BodyProperties = new BodyProperties()
                {
                    Rotation = 0,
                    ParagraphSpacing = true,
                    TextVerticalOverflow = ST_TextVertOverflowType.Ellipsis,
                    VerticalText = ST_TextVerticalType.Horizontal,
                    TextWrappingType = ST_TextWrappingType.Square,
                    Anchor = ST_TextAnchoringType.Center,
                    AnchorCenter = true
                },
                TextListStyles = new TextListStyles()
            };

            Paragraph paragraph = new Paragraph();
            SchemeColorModel cmodel = new SchemeColorModel();
            paragraph.Properties = new ParagraphProperties()
            {
                RunProperties = new RunProperties()
                {
                    FontSize = 1400,
                    Bold = false,
                    Italic = false,
                    UnderlineType = ST_TextUnderlineType.None,
                    Strikethrough = ST_TextStrikeType.NoStrike,
                    Kerning = 1200,
                    Spacing = 0,
                    Baseline = 0,
                    FillProperties = new SolidFill()
                    {
                        ColorElement = cmodel
                    },
                    LatinFont = new Font() { Typeface = "+mn-lt" },
                    EastAsianFont = new Font() { Typeface = "+mn-ea" },
                    ComplexScriptFont = new Font() { Typeface = "+mn-cs" }
                }
            };
            cmodel.ColorValue = ST_SchemeColorVal.TextColor1;
            cmodel.ColorElements.Add(new LuminanceModulation() { Value = 65000 });
            cmodel.ColorElements.Add(new LuminanceOffset() { Value = 35000 });
            paragraph.RunProperties = new RunProperties() { LanguageId = "hu-HU" };
            textProperties.Paragraphs.Add(paragraph);

            Title titleElement = new Title()
            {
                ChartText = new Text()
                {
                    TextElement = new RichText()
                    {
                        BodyProperties = new BodyProperties()
                    }
                },
                Overlay = new NullableValue<bool>(false),
                ShapeProperties = shapeProperties,
                TextProperties = textProperties
            };
            Paragraph p = new Paragraph();
            Run r = new Run() { Text = new SimpleText(title) };
            p.Elements.Add(r);
            (titleElement.ChartText.TextElement as RichText).Paragraphs.Add(p);

            return titleElement;
        }

        private static Legend CreateLegend()
        {
            ShapeProperties shapeProperties = new ShapeProperties()
            {
                FillProperty = new NoFill(),
                Line = new Line() { LineFillProperty = new NoFill() },
                EffectProperty = new EffectList()
            };

            TextProperties textProperties = new TextProperties()
            {
                BodyProperties = new BodyProperties()
                {
                    Rotation = 0,
                    ParagraphSpacing = true,
                    TextVerticalOverflow = ST_TextVertOverflowType.Ellipsis,
                    VerticalText = ST_TextVerticalType.Horizontal,
                    TextWrappingType = ST_TextWrappingType.Square,
                    Anchor = ST_TextAnchoringType.Center,
                    AnchorCenter = true
                },
                TextListStyles = new TextListStyles()
            };

            SchemeColorModel cmodel = new SchemeColorModel();
            cmodel.ColorValue = ST_SchemeColorVal.TextColor1;
            cmodel.ColorElements.Add(new LuminanceModulation() { Value = 65000 });
            cmodel.ColorElements.Add(new LuminanceOffset() { Value = 35000 });

            Paragraph paragraph = new Paragraph();
            paragraph.Properties = new ParagraphProperties()
            {
                RunProperties = new RunProperties()
                {
                    FontSize = 900,
                    Bold = false,
                    Italic = false,
                    UnderlineType = ST_TextUnderlineType.None,
                    Strikethrough = ST_TextStrikeType.NoStrike,
                    Kerning = 1200,
                    Spacing = 0,
                    Baseline = 0,
                    FillProperties = new SolidFill()
                    {
                        ColorElement = cmodel
                    },
                    LatinFont = new Font() { Typeface = "+mn-lt" },
                    EastAsianFont = new Font() { Typeface = "+mn-ea" },
                    ComplexScriptFont = new Font() { Typeface = "+mn-cs" }
                }
            };
            paragraph.RunProperties = new RunProperties() { LanguageId = "hu-HU" };
            textProperties.Paragraphs.Add(paragraph);

            return new Legend
            {
                Overlay = new NullableValue<bool>(false),
                ShapeProperties = shapeProperties,
                TextProperties = textProperties,
                LegendPosition = new NullableValue<ST_LegendPos>(ST_LegendPos.Bottom)
            };
        }

        private static PlotArea CreatePlotArea(uint catAxisId, uint valueAxisId, ChartDescriptor descriptor)
        {
            SchemeColorModel color1 = new SchemeColorModel();
            color1.ColorValue = ST_SchemeColorVal.TextColor1;
            color1.ColorElements.Add(new LuminanceModulation() { Value = 15000 });
            color1.ColorElements.Add(new LuminanceOffset() { Value = 85000 });

            SchemeColorModel color2 = new SchemeColorModel();
            color2.ColorValue = ST_SchemeColorVal.TextColor1;
            color2.ColorElements.Add(new LuminanceModulation() { Value = 65000 });
            color2.ColorElements.Add(new LuminanceOffset() { Value = 35000 });

            ShapeProperties commonShape = new ShapeProperties()
            {
                FillProperty = new NoFill(),
                Line = new Line()
                {
                    Width = 9525,
                    EndingType = ST_LineCap.Flat,
                    Compound = ST_CompoundLine.Single,
                    Alignment = ST_PenAlignment.Center,
                    LineJoinProperty = new RoundLineJoin(),
                    LineFillProperty = new SolidFill() { ColorElement = color1 }
                },
                EffectProperty = new EffectList()
            };

            Paragraph commonParagraph = new Paragraph()
            {
                Properties = new ParagraphProperties()
                {
                    RunProperties = new RunProperties()
                    {
                        FontSize = 900,
                        Bold = false,
                        Italic = false,
                        UnderlineType = ST_TextUnderlineType.None,
                        Strikethrough = ST_TextStrikeType.NoStrike,
                        Kerning = 1200,
                        Baseline = 0,
                        FillProperties = new SolidFill()
                        {
                            ColorElement = color2
                        },
                        LatinFont = new Font() { Typeface = "+mn-lt" },
                        EastAsianFont = new Font() { Typeface = "+mn-ea" },
                        ComplexScriptFont = new Font() { Typeface = "+mn-cs" }
                    }
                },
                RunProperties = new RunProperties() { LanguageId = "hu-HU" }
            };

            PlotArea plotArea = new PlotArea()
            {
                Layout = new Layout(),
                ShapeProperties = new ShapeProperties()
                {
                    FillProperty = new NoFill(),
                    Line = new Line() { LineFillProperty = new NoFill() },
                    EffectProperty = new EffectList()
                }
            };

            CategoryAxis xaxis = new CategoryAxis()
            {
                AxisId = new RequiredValue<int>((int)catAxisId),
                Scaling = new Scaling()
                {
                    Orientation = new NullableValue<ST_Orientation>(ST_Orientation.MinMax)
                },
                Delete = new NullableValue<bool>(false),
                AxisPosition = new RequiredValue<ST_AxPos>(ST_AxPos.Bottom),
                NumberFormat = new NumberFormat()
                {
                    FormatCode = "General",
                    SourceLinked = true
                },
                MajorTickMark = new NullableValue<ST_TickMark>(ST_TickMark.None),
                MinorTickMark = new NullableValue<ST_TickMark>(ST_TickMark.None),
                TickLabelPosition = new NullableValue<ST_TickLblPos>(ST_TickLblPos.Low),
                ShapeProperties = commonShape,
                TextProperties = new TextProperties()
                {
                    BodyProperties = new BodyProperties()
                    {
                        Rotation = -60000000,
                        ParagraphSpacing = true,
                        TextVerticalOverflow = ST_TextVertOverflowType.Ellipsis,
                        VerticalText = ST_TextVerticalType.Horizontal
                    },
                    TextListStyles = new TextListStyles()
                },
                CrossAxesId = new RequiredValue<uint>(valueAxisId),
                Crosses = new Crosses() { Value = ST_Crosses.AutoZero },
                Auto = new NullableValue<bool>(true),
                LabelOffset = new NullableValue<uint>(100),
                NoMultiLevelLabels = new NullableValue<bool>(false),
                LabelAlignment = new RequiredValue<ST_LblAlgn>(ST_LblAlgn.Center)
            };
            xaxis.TextProperties.Paragraphs.Add(commonParagraph);

            ValueAxis va = new ValueAxis()
            {
                AxisId = new RequiredValue<int>((int)valueAxisId),
                Scaling = new Scaling()
                {
                    Orientation = new NullableValue<ST_Orientation>(ST_Orientation.MinMax)
                },
                Delete = new NullableValue<bool>(false),
                AxisPosition = new RequiredValue<ST_AxPos>(ST_AxPos.Left),
                MajorGridlines = new Gridlines()
                {
                    ShapeProperties = commonShape
                },
                NumberFormat = new NumberFormat()
                {
                    FormatCode = "General",
                    SourceLinked = true
                },
                MajorTickMark = new NullableValue<ST_TickMark>(ST_TickMark.None),
                MinorTickMark = new NullableValue<ST_TickMark>(ST_TickMark.None),
                TickLabelPosition = new NullableValue<ST_TickLblPos>(ST_TickLblPos.NextTo),
                ShapeProperties = commonShape,
                TextProperties = new TextProperties()
                {
                    BodyProperties = new BodyProperties()
                    {
                        Rotation = -60000000,
                        ParagraphSpacing = true,
                        TextVerticalOverflow = ST_TextVertOverflowType.Ellipsis,
                        VerticalText = ST_TextVerticalType.Horizontal
                    },
                    TextListStyles = new TextListStyles()
                },
                CrossAxesId = new RequiredValue<uint>(catAxisId),
                Crosses = new Crosses(),
                CrossBetween = new RequiredValue<ST_CrossBetween>(ST_CrossBetween.Between)
            };

            va.DisplayUnits = new DisplayUnits()
            {
                DisplayUnit = new CustomDisplayUnit() { Value = 1.0 },
                Label = new DisplayUnitsLabel()
                {
                    Text = new Text()
                    {
                        TextElement = new RichText(descriptor.Unit,
                            new RunProperties() { FontSize = 1000 })
                        {
                            BodyProperties = new BodyProperties()
                        }
                    }
                }
            };

            va.TextProperties.Paragraphs.Add(commonParagraph);

            plotArea.Axes.Add(xaxis);
            plotArea.Axes.Add(va);

            return plotArea;
        }

        private static LineChart CreateLineChart(
            uint catAxisId, uint valueAxisId, ChartDescriptor descriptor, uint startIndex)
        {
            LineChart lineChart = new LineChart()
            {
                Grouping = new NullableValue<ST_Grouping>(ST_Grouping.Standard),
                VaryColors = new NullableValue<bool>(false),
                DataLabels = new DataLabels()
                {
                    ShowLegendKey = new NullableValue<bool>(false),
                    ShowValue = new NullableValue<bool>(false),
                    ShowCategoryName = new NullableValue<bool>(false),
                    ShowSeriesName = new NullableValue<bool>(false),
                    ShowPercent = new NullableValue<bool>(false),
                    ShowBubbleSize = new NullableValue<bool>(false)
                },
                Smooth = new NullableValue<bool>(false),
                Axis1Id = new RequiredValue<uint>(catAxisId),
                Axis2Id = new RequiredValue<uint>(valueAxisId)
            };

            uint index = startIndex;
            foreach (var serie in descriptor.Series)
            {
                lineChart.Series.Add(CreateLineSerie(index++, serie, descriptor.XValues));
            }

            return lineChart;
        }
        
        private static LineSeries CreateLineSerie(uint index, Serie serie, string[] xValues)
        {
            SchemeColorModel serieColor = new SchemeColorModel();
            serieColor.ColorValue = ST_SchemeColorVal.AccentColor1;
            serieColor.ColorElements.Add(new Red() { Value = (int)(((float)serie.Color.R / 255f) * 100000) });
            serieColor.ColorElements.Add(new Blue() { Value = (int)(((float)serie.Color.G / 255f) * 100000) });
            serieColor.ColorElements.Add(new Green() { Value = (int)(((float)serie.Color.B / 255f) * 100000) });

            StringReference serieCat = new StringReference()
            {
                Formula = new SimpleText(serie.References["x"])
            };
            serieCat.NumberCache = new StringLiteral()
            {
                PointCount = new RequiredValue<uint>((uint)xValues.Length)
            };
            for (int i = 0; i < xValues.Length; ++i)
            {
                serieCat.NumberCache.Points.Add(new DocumentFormat.OpenXml.Lite.DrawingML.Chart.Point()
                {
                    Index = (uint)(i + 1),
                    Value = new SimpleText(xValues[i])
                });
            }

            NumberReference serieVal = new NumberReference()
            {
                Formula = new SimpleText(serie.References["y"])
            };
            serieVal.NumberCache = new NumberLiteral()
            {
                PointCount = new RequiredValue<uint>((uint)serie.YValues.Length)
            };
            for (int i = 0; i < serie.YValues.Length; ++i)
            {
                serieVal.NumberCache.Points.Add(new DocumentFormat.OpenXml.Lite.DrawingML.Chart.Point()
                {
                    Index = (uint)(i + 1),
                    FormatCode = "General",
                    Value = new SimpleText(serie.YValues[i])
                });
            }

            LineSeries lineSerie = new LineSeries()
            {
                Index = new RequiredValue<uint>(index),
                Order = new RequiredValue<uint>(index),
                Marker = new Marker()
                {
                    Symbol = new RequiredValue<ST_MarkerStyle>(ST_MarkerStyle.None)
                },
                Smooth = new NullableValue<bool>(false),
                Text = new Text()
                {
                    TextElement = new TextValue() { Value = serie.Label }
                },
                ShapeProperties = new ShapeProperties()
                {
                    Line = new Line()
                    {
                        Width = 28575,
                        EndingType = ST_LineCap.Round,
                        LineFillProperty = new SolidFill()
                        {
                            ColorElement = serieColor
                        },
                        LineJoinProperty = new RoundLineJoin()
                    },
                    EffectProperty = new EffectList()
                },
                CategoryAxisData = new Category()
                {
                    Element = serieCat
                },
                Value = new Value()
                {
                    Val = serieVal
                }
            };

            return lineSerie;
        }
    }
}
